﻿#region

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using RestSharp;

#endregion

namespace wsy.ext.restsharp
{
    public static class Extend
    {
        public static Task DownloadDataByProgress(this IRestClient rc, IRestRequest req, string file,
            IProgress<DownloadDataStruct> progress)
        {
            return Task.Factory.StartNew(() =>
            {
                var finalFileSize = rc.Head(req).ContentLength;
                req.Method = Method.GET;
                DownloadDataStruct data = new DownloadDataStruct();
                data.FinalFileSize = finalFileSize;
                using (var writer = new HikFileStream(file))
                {
                    writer.Progress += (w, arg) =>
                    {
                        data.CurrentSize = writer.CurrentSize;
                        progress.Report(data);
                    };
                    req.ResponseWriter = responseStream => responseStream.CopyTo(writer);
                    rc.DownloadData(req);
                }
            });
        }

        public interface IProgress<T>
        {
            void Report(T data);
        }

        public class Progress<T> : IProgress<T>
        {
            private readonly SynchronizationContext _context;

            public Progress()
            {
                _context = SynchronizationContext.Current
                           ?? new SynchronizationContext();
            }

            public Progress(Action<T> action)
                : this()
            {
                ProgressReported += action;
            }

            void IProgress<T>.Report(T data)
            {
                var action = ProgressReported;
                if (action != null)
                    _context.Post(arg => action((T) arg), data);
            }

            public event Action<T> ProgressReported;
        }

        public struct DownloadDataStruct
        {
            public long CurrentSize { get; set; }
            public long FinalFileSize { get; set; }
        }

        private class HikFileStream : FileStream
        {
            public HikFileStream(string path) : base(path, FileMode.Create, FileAccess.Write, FileShare.None)
            {
            }

            public long CurrentSize { get; private set; }


            public event EventHandler Progress;

            public override void Write(byte[] array, int offset, int count)
            {
                base.Write(array, offset, count);
                CurrentSize += count;
                var h = Progress;
                h?.Invoke(this, EventArgs.Empty); //WARN: THIS SHOULD RETURNS ASAP!
            }
        }
    }
}